package com.acme.bankapp.util;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Blocking queue written via {@link Lock}
 * @author Gregory Sobko
 */
public class LockingQueue<T> implements ConcurrentQueue<T> {

    private List<T> storage=new LinkedList<>();
    private int limit;
    private boolean isClosed = false;

    private Lock queueLock = new ReentrantLock();
    private Condition somethingWasPushed = queueLock.newCondition();
    private Condition somethingWasPopped = queueLock.newCondition();

    public LockingQueue(int limit) {
        this.limit = limit;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T pop() throws InterruptedException {
        try{
            queueLock.lock();
            while(storage.isEmpty()){
                somethingWasPushed.await();
            }
            somethingWasPopped.signal();
            return storage.remove(0);
        }finally {
            queueLock.unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T tryPop() {
        try {
            queueLock.lock();
            if(!storage.isEmpty()){
                somethingWasPopped.signal();
                return storage.remove(0);
            }else {
                return null;
            }
        }finally {
            queueLock.unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void push(T element) throws InterruptedException {
        try{
            if(isClosed){
                throw new IllegalStateException("LockingQueue is locked!");
            }
            queueLock.lock();
            while (storage.size() >= limit){
                somethingWasPopped.await();
            }
            somethingWasPushed.signal();
            storage.add(element);
        }finally {
            queueLock.unlock();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void close() {
        try{
            queueLock.lock();
            this.isClosed = true;
        }finally {
            queueLock.unlock();
        }
    }
}
